/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	OutputMergerStage.cpp
*
*	Comments	-	See OutputMergerStage.h
*
**************************************************************************************/
#include "../Include/OutputMergerStage.h"
#include "../Include/Renderer.h"
#include "../Include/BlendState.h"
#include "../Include/ShaderResource.h"
#include "../Include/DepthStencilState.h"
#include "../Include/DX11Mapping.h"

namespace Pulse
{
	OutputMergerState::OutputMergerState( void )
	{
		ClearState();
	}

	void OutputMergerState::ClearState( void )
	{
		m_pBlendState = PSX_NULL;
		m_blendFactors[0] = 1.0f;
		m_blendFactors[1] = 1.0f;
		m_blendFactors[2] = 1.0f;
		m_blendFactors[3] = 1.0f;
		m_sampleMask = 0xffffffff;
		m_pDepthStencilState = PSX_NULL;
		m_stencilRef = 1;
		PSX_ZeroMem( m_pRenderTargets, sizeof(m_pRenderTargets) );
		m_pDepthStencil = PSX_NULL;
		PSX_ZeroMem( m_pD3DRenderTargets, sizeof(m_pD3DRenderTargets) );
	}

	OutputMergerStage::OutputMergerStage( void )
	{
		ClearStates();
	}

	OutputMergerStage::~OutputMergerStage( void )
	{

	}

	void OutputMergerStage::SetBlendState( BlendState *pBlendState )
	{
		m_desiredState.m_pBlendState = pBlendState;
	}

	void OutputMergerStage::SetBlendFactors( FLOAT r, FLOAT g, FLOAT b, FLOAT a )
	{
		m_desiredState.m_blendFactors[0] = r;
		m_desiredState.m_blendFactors[1] = g;
		m_desiredState.m_blendFactors[2] = b;
		m_desiredState.m_blendFactors[3] = a;
	}

	void OutputMergerStage::SetBlendFactors( const FLOAT *pBlendFactors )
	{
		PSX_MemCopyPerByte( m_desiredState.m_blendFactors, pBlendFactors, sizeof(FLOAT) * 4 );
	}

	void OutputMergerStage::SetSampleMask( const UINT sampleMask )
	{
		m_desiredState.m_sampleMask = sampleMask;
	}

	void OutputMergerStage::SetDepthStencilState( DepthStencilState *pDepthStencilState )
	{
		m_desiredState.m_pDepthStencilState = pDepthStencilState;
	}

	void OutputMergerStage::SetStencilRef( UINT ref )
	{
		m_desiredState.m_stencilRef = ref;
	}

	void OutputMergerStage::SetRenderTarget( const UINT slot, ShaderResource *pRT )
	{
		m_desiredState.m_pRenderTargets[slot] = pRT;
		m_desiredState.m_pD3DRenderTargets[slot] = (ID3D11RenderTargetView*)pRT->GetNakedRenderTargetResource();
	}

	void OutputMergerStage::SetRenderTarget( const UINT numTargets, ShaderResource **pRTS )
	{
		const SIZE_T size = sizeof( ShaderResource* );
		PSX_MemCopyPerByte( m_desiredState.m_pRenderTargets, pRTS, size * numTargets ); 
		
		for ( SIZE_T i = 0; i < numTargets; ++i )
		{
			if ( pRTS[i] == PSX_NULL )
				break;

			m_desiredState.m_pD3DRenderTargets[i] = (ID3D11RenderTargetView*)pRTS[i]->GetNakedRenderTargetResource();
		}
	}

	void OutputMergerStage::SetDepthStencil( ShaderResource *pRT )
	{
		m_desiredState.m_pDepthStencil = pRT;
	}

	const UINT OutputMergerStage::GetNumRenderTargets( void ) const
	{
		UINT count = 0;

		for ( UINT i = 0; i < PSX_NUM_RENDER_TARGET_SLOT; ++i )
		{
			if ( m_currentState.m_pRenderTargets[i] )
			{
				++count;
			}
			else
			{
				break;
			}
		}

		return count;
	}

	void OutputMergerStage::BindStates( void )
	{
		BindBlendState();
		BindDepthStencilState();
		BindRenderTargets();
	}

	void OutputMergerStage::ClearStates( void )
	{
		m_desiredState.ClearState();
	}

	void OutputMergerStage::BindBlendState( void )
	{
		ID3D11DeviceContext *pDC = (ID3D11DeviceContext*)m_pRenderer->GetNakedDeviceContext();

		pDC->OMSetBlendState( (ID3D11BlendState*)(m_desiredState.m_pBlendState ? m_desiredState.m_pBlendState->GetNakedBlendState() : PSX_NULL),
			m_desiredState.m_blendFactors, m_desiredState.m_sampleMask );

		m_currentState.m_pBlendState = m_desiredState.m_pBlendState;
		PSX_MemCopyPerByte( m_currentState.m_blendFactors, m_desiredState.m_blendFactors, sizeof(m_currentState.m_blendFactors) );
		m_currentState.m_sampleMask = m_desiredState.m_sampleMask;

	}

	void OutputMergerStage::BindDepthStencilState( void )
	{
		ID3D11DeviceContext *pDC = (ID3D11DeviceContext*)m_pRenderer->GetNakedDeviceContext();
		pDC->OMSetDepthStencilState( (ID3D11DepthStencilState*)(m_desiredState.m_pDepthStencilState ? m_desiredState.m_pDepthStencilState->GetNakedDepthStencilState() : PSX_NULL ),
			m_desiredState.m_stencilRef );

		m_currentState.m_pDepthStencilState = m_desiredState.m_pDepthStencilState;
		m_currentState.m_stencilRef = m_desiredState.m_stencilRef;
	}

	void OutputMergerStage::BindRenderTargets( void )
	{
		ID3D11DeviceContext *pDC = (ID3D11DeviceContext*)m_pRenderer->GetNakedDeviceContext();
		ShaderResource *pDepthStencil = m_desiredState.m_pDepthStencil;

		pDC->OMSetRenderTargets( PSX_NUM_RENDER_TARGET_SLOT, m_desiredState.m_pD3DRenderTargets, 
			(ID3D11DepthStencilView*)(pDepthStencil ? pDepthStencil->GetNakedDepthStencilResource() : PSX_NULL) );

		PSX_MemCopyPerByte( m_currentState.m_pRenderTargets, m_desiredState.m_pRenderTargets, sizeof(m_currentState.m_pRenderTargets) );
		m_currentState.m_pDepthStencil = m_desiredState.m_pDepthStencil;
		PSX_MemCopyPerByte( m_currentState.m_pD3DRenderTargets, m_desiredState.m_pD3DRenderTargets, sizeof(m_currentState.m_pD3DRenderTargets) );
	}

	void OutputMergerStage::ClearBuffers( const Color *pClearColor, FLOAT depth, UINT stencil )
	{
		ID3D11DeviceContext *pDC = (ID3D11DeviceContext*)m_pRenderer->GetNakedDeviceContext();
		const UINT numRenderTargets = GetNumRenderTargets();
		const FLOAT clearColor[4] = { pClearColor->GetRedF(), pClearColor->GetGreenF(), pClearColor->GetBlueF(), pClearColor->GetAlphaF() };
		
		for ( UINT i = 0; i < numRenderTargets; ++i )
		{
			pDC->ClearRenderTargetView( m_currentState.m_pD3DRenderTargets[i], clearColor );
		}

		if ( m_currentState.m_pDepthStencil )
		{
			// TODO: Update the clear flags to also include stencil if needed D3D11_CLEAR_STENCIL.
			pDC->ClearDepthStencilView( (ID3D11DepthStencilView*)m_desiredState.m_pDepthStencil->GetNakedDepthStencilResource(),
				D3D11_CLEAR_DEPTH, depth, stencil );
		}
	}

}